/*
 * CCVisu is a tool for visual graph clustering
 * and general force-directed graph layout.
 * This file is part of CCVisu.
 *
 * Copyright (C) 2005-2012  Dirk Beyer
 *
 * CCVisu is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * CCVisu is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with CCVisu; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Please find the GNU Lesser General Public License in file
 * license_lgpl.txt or http://www.gnu.org/licenses/lgpl.txt
 *
 * Dirk Beyer    (firstname.lastname@uni-passau.de)
 * University of Passau, Bavaria, Germany
 */
package org.sosy_lab.ccvisu.graph;

import java.util.Collection;

public class Position {
  public float x = 0.0f;
  public float y = 0.0f;
  public float z = 0.0f;

  public Position() {
  }

  public Position(float x, float y, float z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }

  public Position(Position position) {
    this(position.x, position.y, position.z);
  }

  public float getX() {
    return x;
  }

  public float getY() {
    return y;
  }

  public float getZ() {
    return z;
  }

  public boolean equals(Position position) {
    return (x == position.x && y == position.y && z == position.z);
  }

  public boolean lessEq(Position position) {
    return (x <= position.x && y <= position.y && z <= position.z);
  }

  public void add(Position position) {
    x += position.x;
    y += position.y;
    z += position.z;
  }

  public void subtract(Position position) {
    x -= position.x;
    y -= position.y;
    z -= position.z;
  }

  public void mult(Position position) {
    x *= position.x;
    y *= position.y;
    z *= position.z;
  }

  public void div(Position position) {
    x /= position.x;
    y /= position.y;
    z /= position.z;
  }

  public void mult(float scalar) {
    x *= scalar;
    y *= scalar;
    z *= scalar;
  }

  public void div(float scalar) {
    x /= scalar;
    y /= scalar;
    z /= scalar;
  }

  public static Position add(Position position, float scalar) {
    Position result = new Position(position);

    result.x += scalar;
    result.y += scalar;
    result.z += scalar;

    return result;
  }

  public static Position subtract(Position position, float scalar) {
    Position result = new Position(position);

    result.x -= scalar;
    result.y -= scalar;
    result.z -= scalar;

    return result;
  }

  public static Position mult(Position position, float scalar) {
    Position result = new Position(position);

    result.x *= scalar;
    result.y *= scalar;
    result.z *= scalar;

    return result;
  }

  public static Position div(Position position, float scalar) {
    Position result = new Position(position);

    result.x /= scalar;
    result.y /= scalar;
    result.z /= scalar;

    return result;
  }

  public static Position add(Position pos1, Position pos2) {
    Position result = new Position(pos1);

    result.x += pos2.x;
    result.y += pos2.y;
    result.z += pos2.z;

    return result;
  }

  public static Position subtract(Position pos1, Position pos2) {
    Position result = new Position(pos1);

    result.x -= pos2.x;
    result.y -= pos2.y;
    result.z -= pos2.z;

    return result;
  }

  public static Position mult(Position pos1, Position pos2) {
    Position result = new Position(pos1);

    result.x *= pos2.x;
    result.y *= pos2.y;
    result.z *= pos2.z;

    return result;
  }

  public static Position div(Position pos1, Position pos2) {
    Position result = new Position(pos1);

    result.x /= pos2.x;
    result.y /= pos2.y;
    result.z /= pos2.z;

    return result;
  }

  public static Position min(Collection<GraphVertex> collection,
      boolean restrictToVisible, boolean includeAux) {

    Position minPos = new Position(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);

    for (GraphVertex vertex : collection) {
      if ((!restrictToVisible || vertex.isShowVertex())
          && (includeAux || !vertex.isAuxiliary())) {

          minPos.x = Math.min(minPos.x, vertex.getPosition().x);
          minPos.y = Math.min(minPos.y, vertex.getPosition().y);
          minPos.z = Math.min(minPos.z, vertex.getPosition().z);

      }
    }
    return minPos;
  }

  public static Position min(Collection<GraphVertex> collection, boolean restrictToVisible) {
    return min(collection, restrictToVisible, true);
  }

  public static Position max(Collection<GraphVertex> collection, boolean restrictToVisible, boolean includeAux) {
    Position maxPos = new Position(-Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE);
    for (GraphVertex vertex : collection) {
      if ((!restrictToVisible || vertex.isShowVertex())
          && (includeAux || !vertex.isAuxiliary())) {

        maxPos.x = Math.max(maxPos.x, vertex.getPosition().x);
        maxPos.y = Math.max(maxPos.y, vertex.getPosition().y);
        maxPos.z = Math.max(maxPos.z, vertex.getPosition().z);
      }
    }
    return maxPos;
  }

  public static Position max(Collection<GraphVertex> collection, boolean restrictToVisible) {
    return max(collection, restrictToVisible, true);
  }

  /**
   * Returns the maximum over each dimension of the width of two points.
   *
   * @return Maximum over each dimension of the width of two points.
   */
  public static float width(Position pos1, Position pos2) {
    Position diffPos = Position.subtract(pos1, pos2);
    float width = Math.max(diffPos.x, diffPos.y);
    width = Math.max(diffPos.z, width);

    return width;
  }

  /**
   * Returns the Euclidean distance between the specified positions.
   *
   * @return Euclidean distance between the specified positions.
   */
  public float distanceTo(Position position) {
    float xDiff = this.x - position.x;
    float yDiff = this.y - position.y;
    float zDiff = this.z - position.z;

    return (float) Math.sqrt(xDiff * xDiff
                             + yDiff * yDiff
                             + zDiff * zDiff);
  }
}
